Adding full support for custom materials to GltfLoader#2725
Conversation
If multiple GLTF extensions are present on an element, the CustomContentManager now processes all supported ones instead of just the first one.
…al creation process
* Step 1: Collect all material data in the new GltfMaterialData object * Step 1.1: GltfLoader reads all standard GLTF parameters * Step 1.2: ExtensionLoader and ExtrasLoader can read additional GLTF parameters * Step 2: Find a matching GltfMaterialFactory and use it to create the material
…ensionLoaders * Retains the old MaterialAdapter handling for backward compatibility
…d material * Added both material factories as the default ones to the GltfLoader
Reason: Setting default values directly in GltfMaterialData obscures whether a parameter is actually defined in the glTF material. The presence of material parameters may be relevant for some material factories. Therefore, default values should be set by the material factories themselves.
There was a problem hiding this comment.
Code Review
This pull request introduces a new material factory system for the GLTF loader, deprecating the legacy material adapter system. The new architecture uses GltfMaterialData and GltfMaterialFactory to provide a more extensible approach to material creation. Feedback was provided to correct a typo in the EMISSIV constant names within GltfMaterialData.java to improve code consistency.
There was a problem hiding this comment.
Pull request overview
This PR reworks glTF material creation in GltfLoader by introducing a GltfMaterialFactory pipeline backed by a new GltfMaterialData container, enabling custom material implementations while keeping the legacy MaterialAdapter system as an opt-in fallback for backward compatibility.
Changes:
- Added a factory-based material creation system (
GltfMaterialFactory,GltfMaterialData) with default factories forPBRLightingandUnshaded(KHR_materials_unlit). - Updated extension loaders to populate
GltfMaterialData(and adjustedCustomContentManagerto process multiple extensions per element). - Deprecated legacy
MaterialAdapter-based classes/entry points and added aGltfModelKeyflag to re-enable the legacy path.
Reviewed changes
Copilot reviewed 17 out of 17 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/UnshadedMaterialFactory.java | New factory that creates Unshaded materials from GltfMaterialData (KHR_materials_unlit). |
| jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBRLightingMaterialFactory.java | New factory that creates PBRLighting materials (including spec/gloss extension support). |
| jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfMaterialFactory.java | New SPI interface for pluggable material creation (contains a small Javadoc typo). |
| jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfMaterialData.java | New material-data container and parameter naming conventions for factories/extensions. |
| jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfLoader.java | Integrates factory pipeline, adds registration APIs, adjusts transparency bucketing, and adds legacy fallback switch. |
| jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/CustomContentManager.java | Fix: process multiple extensions for the same element instead of returning after the first. |
| jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/UnlitExtensionLoader.java | Updated to tag GltfMaterialData with unlit extension; legacy adapter path retained/deprecated. |
| jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBRSpecGlossExtensionLoader.java | Updated to populate spec/gloss parameters into GltfMaterialData; legacy path retained/deprecated. |
| jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBREmissiveStrengthExtensionLoader.java | Updated to populate emissive strength into GltfMaterialData; legacy path retained/deprecated. |
| jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfModelKey.java | Adds flag to re-enable legacy adapters and deprecates adapter APIs (needs cache-key equality update). |
| jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfUtils.java | Adds helper to read the legacy-adapter-enabled flag; deprecates adapter accessor. |
| jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/MaterialAdapter.java | Deprecated as part of migration path. |
| jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBRMaterialAdapter.java | Deprecated as part of migration path. |
| jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBRMetalRoughMaterialAdapter.java | Deprecated as part of migration path. |
| jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBRSpecGlossMaterialAdapter.java | Deprecated as part of migration path. |
| jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBREmissiveStrengthMaterialAdapter.java | Deprecated as part of migration path. |
| jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/UnlitMaterialAdapter.java | Deprecated as part of migration path. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
|
||
| setParam(material, "EmissiveMap", gltfMaterialData.getGltfParam(EMISSIVE_TEXTURE_PARAM)); | ||
| setParam(material, "Emissive", gltfMaterialData.getGltfParam(EMISSIVE_COLOR_PARAM), ColorRGBA.Black); | ||
| setParam(material, "EmissiveIntensity", gltfMaterialData.getGltfParam(EMISSIVE_STRENGTH_PARAM)); |
There was a problem hiding this comment.
Default value of 1 was added to "EmissiveIntensity" parameter.
| @@ -526,9 +535,10 @@ public Geometry[] readMeshPrimitives(int meshIndex) throws IOException { | |||
| geom.setMaterial(defaultMat); | |||
There was a problem hiding this comment.
The vertex coloring flag is now also set correctly for the default material.
| public Material readMaterial(int materialIndex, boolean usesVertexColors) throws IOException { | ||
| // Fallback to the old material adapter system, if the legacy flag is set. | ||
| if (GltfUtils.isMaterialAdaptersEnabled(info)) { | ||
| return readMaterialUsingMaterialAdapters(materialIndex); | ||
| } |
There was a problem hiding this comment.
The vertex coloring flag is now also set correctly in the legacy system.
| /** | ||
| * Enables or disables the legacy material adapter system. | ||
| * This should only be used in older projects for backward compatibility. | ||
| */ | ||
| private boolean materialAdaptersEnabled = false; | ||
|
|
||
| @Deprecated | ||
| private Map<String, MaterialAdapter> materialAdapters = new HashMap<>(); | ||
|
|
There was a problem hiding this comment.
materialAdaptersEnabled flag has been added to the equals() and hashCode() method.
| /** | ||
| * A material factory creates {@link Material}s based on the data of a single material from a GLTF file. | ||
| * <p> | ||
| * All material factories have bo be registered at the {@link GltfLoader} by using one of its |
There was a problem hiding this comment.
Typo has been fixed.
| unregisterMaterialFactory(materialFactory.getClass()); | ||
| materialFactoryList.add(0, materialFactory); |
There was a problem hiding this comment.
Indentation has been fixed.
| public Material readMaterial(int materialIndex, boolean usesVertexColors) throws IOException { | ||
| // Fallback to the old material adapter system, if the legacy flag is set. | ||
| if (GltfUtils.isMaterialAdaptersEnabled(info)) { | ||
| return readMaterialUsingMaterialAdapters(materialIndex); | ||
| } | ||
|
|
||
| assertNotNull(materials, "There is no material defined yet a mesh references one"); | ||
| JsonObject materialJson = materials.get(materialIndex).getAsJsonObject(); | ||
|
|
||
| GltfMaterialData gltfMaterialData = readStandardMaterialParameters(materialJson); | ||
| gltfMaterialData.setHasVertexColors(usesVertexColors); | ||
| gltfMaterialData = customContentManager.readExtensionAndExtras("material", materialJson, gltfMaterialData); | ||
| return createMaterial(gltfMaterialData, materialIndex); |
There was a problem hiding this comment.
Added many new tests for various material parameter combinations to the GltfLoaderTest.
|
I noticed that PBR materials using the "KHR_materials_emissive_strength" extension were broken. This was caused by the legacy part of the PBREmissiveStrengthExtensionLoader, which always replaced the previous MaterialAdapter with its own. Unfortunately, the PBREmissiveStrengthMaterialAdapter did not handle any parameters of the default metallic-roughness system, so those parameters were lost after the replacement. Therefore, I fixed the old implementation by making it additive instead of replacing the existing adapter. |
Reworked the material creation process in
GltfLoader, to allow for custom materials to be created.Supporting a custom material definition
To support a new material definition, one simply needs to implement a new
GltfMaterialFactoryand register it with theGltfLoader. It is important to consider the overall order of all registered material factories, because only the first factory that accepts a given material data instance will be used. By default, there are already two material factories: one for thePBRLightingmaterial and one for theUnshadedmaterial.One special use case would be when someone creates a copy of the standard
PBRLightingmaterial to add additional functionality. If none of the original material parameters have been changed (adding new ones is not a problem), one can simply extendPBRLightingMaterialFactoryand modify the material definition path returned by thegetMaterialDefPath()method. Finally, the originalPBRLightingMaterialFactoryjust needs to be replaced in theGltfLoaderwith the custom one. This way, all loaded models will use the extendedPBRLightingmaterial.Backward compatibility
As mentioned earlier, my new system is not backward compatible with regard to custom
MaterialAdapters. Therefore, I have kept the old implementation but disabled it. To reactivate it, a specific flag must be set in theGltfModelKey. This will fully switch back to the old material creation process when loading the corresponding 3D model.However, I believe that keeping the old
MaterialAdaptersystem in the engine is not a good long-term solution. I suggest removing it in one or two future releases. To reflect this, I have already marked most classes, methods, and fields related to the old process as deprecated.Forum discussion:
https://hub.jmonkeyengine.org/t/adding-full-support-for-custom-materials-to-gltfloader/49444
Also fixed some related bugs:
CustomContentManagercan now handle multiple GLTF extensions per elementBlendMode.AlphaAdditiveare now added toQueueBucket.Transparent